using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using lumo.scene;

/* Xana namespace */
namespace lumo
{
    /// <summary>
    /// Main game class used in lumo.
    /// </summary>
    public class LumoComponent : DrawableGameComponent
    {
        private uint _FrameID;
        private GameTime    _GameTime    = null;
        private SpriteBatch _SpriteBatch = null;
        private IScene _CurrentScene = null;
        private IScene _NextScene = null;
        private bool _SceneChanged = false;
        private bool _SkipFrame = false;

        /// <summary>
        /// The Frame ID
        /// </summary>
        public uint FrameID { get { return _FrameID; } }

        /// <summary>
        /// The game time.
        /// </summary>
        public GameTime GameTime { get { return _GameTime; } }

        /// <summary>
        /// The sprite batch.
        /// </summary>
        public SpriteBatch SpriteBatch { get { return _SpriteBatch; } }

        /// <summary>
        /// The sprite batch.
        /// </summary>
        public ContentManager Content { get { return Game.Content; } }

        /// <summary>
        /// Check if skip frame.
        /// </summary>
        public bool SkipFrame { get { return _SkipFrame; } }


        /// <summary>
        /// Initialize the component.
        /// </summary>
        /// <param name="game">The configuration.</param>
        public LumoComponent(Game game) : base(game)
        {
            // Unlimited FPS.
            this._FrameID = 0;
        }

        /// <summary>
        /// Accessor for the current scene.
        /// </summary>
        public IScene Scene
        {
            get
            {
                if (_SceneChanged)
                    return _NextScene;
                return _CurrentScene;
            }
            set
            {
                _SceneChanged = true;
                _NextScene = value;
            }
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            _SpriteBatch = new SpriteBatch(GraphicsDevice);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            _SpriteBatch.Dispose();
            _SpriteBatch = null;
        }

        /// <summary>
        /// Change the scene.
        /// </summary>
        private void ChangeScene()
        {
            if (_CurrentScene != null)
                _CurrentScene.Terminate(this);
            _CurrentScene = _NextScene;
            _NextScene = null;
            _SceneChanged = false;
            if (_CurrentScene != null)
            {
                _CurrentScene.Start(this);
                _SkipFrame = true;
            }
        }

        /// <summary>
        /// Update this component.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            /* Base game.Update */
            base.Update(gameTime);

            // Add a frame.
            ++_FrameID;

            /* Save the game Time. */
            _GameTime = gameTime;

            /* Update the scene */
            if (_SceneChanged)
                ChangeScene();
            else if (_CurrentScene != null)
            {
                _CurrentScene.Update(this);
                _SkipFrame = false;
                if (_SceneChanged)
                    ChangeScene();
            }

            // Exit tha game if needed
            if (_CurrentScene == null)
                this.Game.Exit();
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            /* Call base.Draw */
            base.Draw(gameTime);

            /* Draw the scene. */
            if ((!_SkipFrame) && (_CurrentScene != null))
                _CurrentScene.Draw(this);
        }
    }
}
